package com.zyl.blog.rbac.config;

import com.zyl.blog.common.annotation.Sensitive;
import com.zyl.blog.common.enums.SensitiveType;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.util.ClassUtils;

import java.lang.reflect.Field;
import java.util.*;

/**
 * @version v1.0
 * @ProjectName: Personal_Blog_Server
 * @Package: com.zyl.blog.rbac.config
 * @ClassName: SensitiveFieldCache
 * @Description: 启动时扫描并缓存所有带@Sensitive注解的字段
 * @Author FinalFantasy
 * @Date 2025/9/8-14:10
 */
@Configuration
public class SensitiveFieldCache implements InitializingBean {

    // 缓存：key=目标类（如UserInfoVO.class），value=该类的脱敏字段信息（字段+策略）
    private static final Map<Class<?>, List<SensitiveFieldInfo>> SENSITIVE_FIELD_MAP = new HashMap<>();

    // 要扫描的包路径（根据你的项目调整，如"com.zyl.blog.vo"）
    private static final String SCAN_PACKAGE = "com.zyl.blog.common.domain.rbac.vo";
    private static final String RESOURCE_PATTERN = "/**/*.class";

    @Override
    public void afterPropertiesSet() throws Exception {
        // 1. 扫描指定包下的所有类
        String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                ClassUtils.convertClassNameToResourcePath(SCAN_PACKAGE) + RESOURCE_PATTERN;
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        CachingMetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory();

        // 2. 遍历每个类，提取带@Sensitive的字段
        for (var resource : resolver.getResources(pattern)) {
            MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
            String className = metadataReader.getClassMetadata().getClassName();
            Class<?> clazz = Class.forName(className);

            // 3. 反射获取类的所有字段（包括父类字段，可选）
            List<Field> allFields = new ArrayList<>();
            Class<?> currentClazz = clazz;
            while (currentClazz != null && !Object.class.equals(currentClazz)) {
                allFields.addAll(Arrays.asList(currentClazz.getDeclaredFields()));
                currentClazz = currentClazz.getSuperclass();
            }

            // 4. 筛选带@Sensitive的字段，存入缓存
            List<SensitiveFieldInfo> sensitiveFields = new ArrayList<>();
            for (Field field : allFields) {
                if (field.isAnnotationPresent(Sensitive.class)) {
                    Sensitive annotation = field.getAnnotation(Sensitive.class);
                    field.setAccessible(true); // 避免安全检查，提升反射效率
                    sensitiveFields.add(new SensitiveFieldInfo(field, annotation.type()));
                }
            }

            // 5. 若有脱敏字段，存入缓存
            if (!sensitiveFields.isEmpty()) {
                SENSITIVE_FIELD_MAP.put(clazz, sensitiveFields);
            }
        }
    }

    // 对外提供获取缓存的方法
    public static List<SensitiveFieldInfo> getSensitiveFields(Class<?> clazz) {
        return SENSITIVE_FIELD_MAP.getOrDefault(clazz, Collections.emptyList());
    }

    /**
     * 封装脱敏字段信息：字段对象+脱敏类型
     * @param sensitiveType 假设Sensitive注解的type是枚举类型
     */
    public record SensitiveFieldInfo(Field field, SensitiveType sensitiveType) {

    }
}
